﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using PlayerIO.GameLibrary;
using System.Drawing;

namespace MyGame {
	public class Player : BasePlayer {
        
        public const int INIT_X = 350;
        public const int INIT_Y = 200;
        public const int HAUT = 0;
        public const int DROITE = 1;
        public const int BAS = 2;
        public const int GAUCHE = 3;
        public const int MOVEMENT_SPEED = 10;

        public Player()
        {
            this.X = INIT_X;
            this.Y = INIT_Y;
            this.Width = 80;
            this.Height = 80;

        }

        public int Points
        {
            get;
            set;
        }

        public int X
        {
            get;
            set;
        }

        public int Y
        {
            get;
            set;
        }

        public int Width
        {
            get;
            set;
        }

        public int Height
        {
            get;
            set;
        }

        public void receivePoints(int p_Points)
        {
            this.Points += p_Points;
        }

        public void movePlayer(int p_Direction)
        {
            switch (p_Direction)
			{
				case Player.HAUT :
				{
                    this.Y -= Player.MOVEMENT_SPEED;
                    break;
				}
                case Player.DROITE:
				{
                    this.X += Player.MOVEMENT_SPEED;
					break;
				}
                case Player.BAS:
				{
                    this.Y += Player.MOVEMENT_SPEED;
					break;
				}
                case Player.GAUCHE:
				{
                    this.X -= Player.MOVEMENT_SPEED;
					break;
				}
			}
		}
	}

    public class Monster
    {
        public Monster(int p_Id,int p_Type, int p_Hp,int p_X, int p_Y,int p_MovementSpeed,int p_AttackPower, int p_Width,int p_Height, int p_Points)
        {
            this.Id = p_Id;
            this.X = p_X;
            this.Y = p_Y;
            this.Type = p_Type;
            this.MovementSpeed = p_MovementSpeed;
            this.AttackPower = p_AttackPower;
            this.Hp = p_Hp;
            this.Width = p_Width;
            this.Height = p_Height;
            this.PointsGiven = p_Points;
        }


        public int Id
        {
            get;
            set;
        }

        public int PointsGiven
        {
            get;
            protected set;
        }

        public int X
        {
            get;
            set;
        }

        public int Y
        {
            get;
            set;
        }

        public int MovementSpeed
        {
            get;
            set;
        }

        public int Hp
        {
            get;
            set;
        }

        public int AttackPower
        {
            get;
            set;
        }

        public int Type
        {
            get;
            set;
        }

        public int Width
        {
            get;
            set;
        }

        public int Height
        {
            get;
            set;
        }

        public void receiveHit(int p_Dmg)
        {
            this.Hp -= p_Dmg;
        }

        public void move(int p_Direction)
        {
            switch (p_Direction)
			{
				case Player.HAUT :
				{
                    this.Y -= this.MovementSpeed;
                    break;
				}
                case Player.DROITE:
				{
                    this.X += this.MovementSpeed;
					break;
				}
                case Player.BAS:
				{
                    this.Y += this.MovementSpeed;
					break;
				}
                case Player.GAUCHE:
				{
                    this.X -= this.MovementSpeed;
					break;
				}
			}
        }
    }

    public class Bullet
    {

        public const int BULLET_SPEED = 5;

        public Bullet(int p_Id,int p_X, int p_Y, int p_Width, int p_Height)
        {
            this.Id = p_Id;
            this.X = p_X;
            this.Y = p_Y;
            this.Width = p_Width;
            this.Height = p_Height;
        }

        public int Id
        {
            get;
            set;
        }
        public int X
        {
            get;
            set;
        }

        public int Y
        {
            get;
            set;
        }

        public int Width
        {
            get;
            set;
        }

        public int Height
        {
            get;
            set;
        }

        public bool Move()
        {
            if (this.Y + BULLET_SPEED < GameCode.STAGE_HEIGHT)
                this.Y += BULLET_SPEED;
            //Return true if we remove the bullet
            return (this.Y+BULLET_SPEED >= GameCode.STAGE_HEIGHT);
        }
    }

	[RoomType("MyCode")]
	public class GameCode : Game<Player> {

        //Conserve the max number of monsters that can appear
        public const int MAX_MONSTERS = 10;
        public const int SpawningChance = 10,HYDRALISK=1,ZERGLING=0,ULTRALISK=2;
        public const int STAGE_WIDHT = 850, STAGE_HEIGHT = 666;
        public const int CHANCE_ZERGLING = 60, CHANCE_HYDRALISK = 30, CHANCE_ULTRALISK = 10;
        public const int ZERGLING_HP = 15, HYDRALISK_HP = 30, ULTRALISK_HP = 60;
        public const int ZERGLING_DMG = 5, HYDRALISK_DMG = 15, ULTRALISK_DMG = 30;
        public const int ZERGLING_SPEED = 3, HYDRALISK_SPEED = 2, ULTRALISK_SPEED = 1;
        public const int ZERGLING_HEIGHT = 58, ZERGLING_WIDTH = 95;
        public const int HYDRALISK_HEIGHT = 76, HYDRALISK_WIDTH = 78;
        public const int ULTRALISK_HEIGHT = 187, ULTRALISK_WIDTH = 172;
        public const int PLAYER_DMG = 10;
        public const int BULLET_HEIGHT = 9, BULLET_WIDTH = 9;
        public const int ZERGLING_POINTS = 100, HYDRALISK_POINTS = 200,ULTRALISK_POINTS = 500;
        Random random = new Random((int)DateTime.Now.Ticks);

        //Create a variable to keep track of monsters ID.
        public int MonsterId = 0;
        public int bulletId = 0;
        public int nbMonsters = 0;

        //Create a list of present monster in the server
        public List<Monster> Monsters = new List<Monster>();
        //Create a list of all the bullets present in the server
        public List<Bullet> Bullets = new List<Bullet>();

        //Hold the player that has the highest score
        //public Player m_HighScore;

        protected int RandomNumber(int min, int max)
        {
            return random.Next(min, max);
        }

        protected bool isSpawnMonster()
        {
            int nb = this.RandomNumber(0, 100);
            return (nb < this.PlayerCount * SpawningChance);
        }

        public bool isCollision(Monster m)
        {
            bool collision = false;
            for(int i = 0;i<Bullets.Count;i++)
            {
                Bullet b = Bullets[i];

                if ((m.X < b.X + b.Width) && (m.X + m.Width > b.Width) && (m.Y < b.Y + b.Width) && (m.Y + m.Width > b.Y))
                {
                    collision = true;

                    //Send a message to all that there was a collision
                    Broadcast("BulletHit", b.Id);
                    this.Bullets.Remove(b);
                    break;
                }
            }

            return collision;
        }
        public void checkCollisions()
        {
            for(int i = 0;i<Monsters.Count;i++)
            {
                Monster m = Monsters[i];

                if (isCollision(m))
                {
                    m.receiveHit(PLAYER_DMG);

                    //Check if the monster is dead
                    if (m.Hp <= 0)
                    {
                        //Check if theres a new high Score
                        Broadcast("KillMonster", m.Id);
                            
                        Monsters.Remove(m);
                        this.nbMonsters--;
                    }
                }
            }
        }

        public void GenerateMonster()
        {
            int x = this.RandomNumber(20, 800);

            int y = STAGE_HEIGHT - 45;

            int random = this.RandomNumber(0,100);

            int type = ZERGLING;

            if (random >= 100 - CHANCE_HYDRALISK)
                type = HYDRALISK;
            if (random >= 100 - CHANCE_ULTRALISK)
                type = ULTRALISK;

            //Send a message to everybody that a monsters spawned
            Broadcast("SpawnMonster",MonsterId, type, x, y);

            //Add the monster to the List
            Monster mon = null;
            switch (type)
            {
                case ZERGLING:
                    mon = new Monster(MonsterId,type, ZERGLING_HP, x, y, ZERGLING_SPEED, ZERGLING_DMG,ZERGLING_WIDTH,ZERGLING_HEIGHT,ZERGLING_POINTS);
                    break;
                case HYDRALISK:
                    mon = new Monster(MonsterId,type, HYDRALISK_HP, x, y, HYDRALISK_SPEED, HYDRALISK_DMG,HYDRALISK_WIDTH,HYDRALISK_HEIGHT,HYDRALISK_POINTS);
                    break;
                case ULTRALISK:
                    mon = new Monster(MonsterId,type, ULTRALISK_HP, x, y, ULTRALISK_SPEED, ULTRALISK_DMG,ULTRALISK_WIDTH,ULTRALISK_HEIGHT,ULTRALISK_POINTS);
                    break;
            }
            this.Monsters.Add(mon);
            this.nbMonsters++;

            //We increment the MonsterId variable
            MonsterId++;
        }

		// This method is called when an instance of your the game is created
		public override void GameStarted() {
			// anything you write to the Console will show up in the 
			// output window of the development server
			Console.WriteLine("Game is started: " + RoomId);

			// This is how you setup a timer
			AddTimer(delegate {
                //Console.WriteLine("coucou les amis");
			}, 1000);

            //Timer to generate monsters in the world
            AddTimer(delegate
            {
                //Check if we spawn a monster (check a certain % chance)
                if (this.nbMonsters < MAX_MONSTERS)
                {
                    if (isSpawnMonster())
                        //Tell it to everyone.
                        this.GenerateMonster();
                }
            }, 500);

            //Timer to check for collisions between bullets and monsters.
            AddTimer(delegate
            {
                //Check for collisions
                checkCollisions();
            }, 500);

            //Timer to check move bullets.
            AddTimer(delegate
            {
                //Move all bullets.
                for(int i = 0;i<Bullets.Count;i++)
                {
                    Bullet b = Bullets[i];

                    if (b.Move())
                    {
                        Bullets.Remove(b);
                        Broadcast("BulletHit", b.Id);
                    }
                }

                //Move all monsters
                for (int i = 0; i < Monsters.Count; i++)
                {
                    Monster m = Monsters[i];

                    m.move(Player.HAUT);
                }
            }, 40);
			
			// Debug Example:
			// Sometimes, it can be very usefull to have a graphical representation
			// of the state of your game.
			// An easy way to accomplish this is to setup a timer to update the
			// debug view every 250th second (4 times a second).
			AddTimer(delegate {
				// This will cause the GenerateDebugImage() method to be called
				// so you can draw a grapical version of the game state.
				RefreshDebugView(); 
			}, 250);
		}

		// This method is called when the last player leaves the room, and it's closed down.
		public override void GameClosed() {
			Console.WriteLine("RoomId: " + RoomId);
		}

		// This method is called whenever a player joins the game
		public override void UserJoined(Player player) {

			//Send to a players all the current players that are online
            foreach (Player p in Players)
            {
                if (p.Id != player.Id)
                    player.Send("CurrentUser",p.Id, p.ConnectUserId, p.X, p.Y);
            }
            
            //Send message to all players but the one who sent it.
            foreach (Player p in Players)
            {
                if (p.Id != player.Id)
                    p.Send("UserJoined",player.Id,player.ConnectUserId);
            }

			// this is how you broadcast a message to all players connected to the game
			//Broadcast("UserJoined", player.Id);
		}

		// This method is called when a player leaves the game
		public override void UserLeft(Player player) {
			Broadcast("UserLeft", player.Id);

            //Delete the user in the player list
            foreach (Player p in Players)
            {
                //if (p.Id == player.Id)
                    //remove from the enumerator
            }
		}

		// This method is called when a player sends a message into the server code
		public override void GotMessage(Player player, Message message) {
			switch(message.Type) {
				// This is how you would set a players name when they send in their name in a 
				// "MyNameIs" message
                case "MyNameIs":
                    {
                        player.Send("Name",player.Id,player.ConnectUserId);
                        break;
                    }
                case "start":
                    {
                        Broadcast("start", player.Id, player.ConnectUserId, message.GetInt(0), message.GetInt(1));
                        break;
                    }
                case "Move":
                    {
                        //Tell all player that a user moved
                        int uID = message.GetInt(0);
                        int direction = message.GetInt(1);

                        //Move the player in the server Player list
                        player.movePlayer(direction);

                        Message m = Message.Create("Move", uID, direction);

                        foreach (Player p in Players)
                        {
                            //Send only to the other players than the player who sent the message
                            if (p.Id != uID)
                                p.Send(m);   
                        }
                        break;
                    }
                case "Chat":
                    {
                        //Send a chat message to everybody
                        Broadcast("Chat",player.ConnectUserId+" : "+message.GetString(0));
                        break;
                    }
                case "Fire":
                    {
                        //Add the Bullet to the list
                        Bullet b = new Bullet(bulletId,player.X + (player.Width/2), player.Y + player.Height-4, BULLET_WIDTH, BULLET_HEIGHT);
                        bulletId++; //We increment the bullet id
                        Bullets.Add(b);

                        //Tell each player that the user just fired
                        Broadcast("Fire", player.Id,b.Id, message.GetInt(0));
                        break;
                    }
			}
		}

		Point debugPoint;

		// This method get's called whenever you trigger it by calling the RefreshDebugView() method.
		public override System.Drawing.Image GenerateDebugImage() {
			// we'll just draw 400 by 400 pixels image with the current time, but you can
			// use this to visualize just about anything.
			var image = new Bitmap(400,400);
			using(var g = Graphics.FromImage(image)) {
				// fill the background
				g.FillRectangle(Brushes.Blue, 0, 0, image.Width, image.Height);

				// draw the current time
				g.DrawString(DateTime.Now.ToString(), new Font("Verdana",20F),Brushes.Orange, 10,10);

				// draw a dot based on the DebugPoint variable
				g.FillRectangle(Brushes.Red, debugPoint.X,debugPoint.Y,5,5);
			}
			return image;
		}

		// During development, it's very usefull to be able to cause certain events
		// to occur in your serverside code. If you create a public method with no
		// arguments and add a [DebugAction] attribute like we've down below, a button
		// will be added to the development server. 
		// Whenever you click the button, your code will run.
		[DebugAction("Play", DebugAction.Icon.Play)]
		public void PlayNow() {
			Console.WriteLine("The play button was clicked!");
		}

		// If you use the [DebugAction] attribute on a method with
		// two int arguments, the action will be triggered via the
		// debug view when you click the debug view on a running game.
		[DebugAction("Set Debug Point", DebugAction.Icon.Green)]
		public void SetDebugPoint(int x, int y) {
			debugPoint = new Point(x,y);
		}
	}
}
